gsk: Add GL profiler
authorEmmanuele Bassi <ebassi@gnome.org>
Tue, 12 Jul 2016 13:50:48 +0000 (14:50 +0100)
committerEmmanuele Bassi <ebassi@gnome.org>
Tue, 18 Oct 2016 10:49:08 +0000 (11:49 +0100)
We can use the GL_ARB_timer_query extension (available since OpenGL
3.2, and part of the OpenGL specification since version 3.3) to query
the time elapsed when drawing each frame. This allows us to gather
timing information on our use of the GPU.

gsk/Makefile.am
gsk/gskglprofiler.c [new file with mode: 0644]
gsk/gskglprofilerprivate.h [new file with mode: 0644]
gsk/gskglrenderer.c

index bf95a3ec4d4149f008aa2bc7fe8af21ae368fba8..4c33dadad5d6095a658c5a5e405cd07f09ec8149 100644 (file)
@@ -38,6 +38,7 @@ gsk_private_source_h = \
        gskcairorendererprivate.h \
        gskdebugprivate.h \
        gskgldriverprivate.h \
+       gskglprofilerprivate.h \
        gskglrendererprivate.h \
        gskprivate.h \
        gskrendererprivate.h \
@@ -55,6 +56,7 @@ gsk_source_c = \
        gskcairorenderer.c \
        gskdebug.c \
        gskgldriver.c \
+       gskglprofiler.c \
        gskglrenderer.c \
        gskrenderer.c \
        gskrendernode.c \
diff --git a/gsk/gskglprofiler.c b/gsk/gskglprofiler.c
new file mode 100644 (file)
index 0000000..ac89fac
--- /dev/null
@@ -0,0 +1,108 @@
+#include "config.h"
+
+#include "gskglprofilerprivate.h"
+
+#include <epoxy/gl.h>
+
+#define N_QUERIES       4
+
+struct _GskGLProfiler
+{
+  GObject parent_instance;
+
+  /* Creating GL queries is kind of expensive, so we pay the
+   * price upfront and create a circular buffer of queries
+   */
+  GLuint gl_queries[N_QUERIES];
+  GLuint active_query;
+
+  gboolean has_timer : 1;
+  gboolean first_frame : 1;
+};
+
+G_DEFINE_TYPE (GskGLProfiler, gsk_gl_profiler, G_TYPE_OBJECT)
+
+static void
+gsk_gl_profiler_finalize (GObject *gobject)
+{
+  GskGLProfiler *self = GSK_GL_PROFILER (gobject);
+
+  glDeleteQueries (N_QUERIES, self->gl_queries);
+
+  G_OBJECT_CLASS (gsk_gl_profiler_parent_class)->finalize (gobject);
+}
+
+static void
+gsk_gl_profiler_class_init (GskGLProfilerClass *klass)
+{
+  G_OBJECT_CLASS (klass)->finalize = gsk_gl_profiler_finalize;
+}
+
+static void
+gsk_gl_profiler_init (GskGLProfiler *self)
+{
+  glGenQueries (N_QUERIES, self->gl_queries);
+
+  self->first_frame = TRUE;
+  self->has_timer = epoxy_has_gl_extension ("GL_ARB_timer_query");
+}
+
+GskGLProfiler *
+gsk_gl_profiler_new (void)
+{
+  return g_object_new (GSK_TYPE_GL_PROFILER, NULL);
+}
+
+void
+gsk_gl_profiler_begin_gpu_region (GskGLProfiler *profiler)
+{
+  GLuint query_id;
+
+  g_return_if_fail (GSK_IS_GL_PROFILER (profiler));
+
+  if (!profiler->has_timer)
+    return;
+
+  query_id = profiler->gl_queries[profiler->active_query];
+  glBeginQuery (GL_TIME_ELAPSED, query_id);
+}
+
+guint64
+gsk_gl_profiler_end_gpu_region (GskGLProfiler *profiler)
+{
+  GLuint last_query_id;
+  GLint res;
+  GLuint64 elapsed;
+
+  g_return_val_if_fail (GSK_IS_GL_PROFILER (profiler), 0);
+
+  if (!profiler->has_timer)
+    return 0;
+
+  glEndQuery (GL_TIME_ELAPSED);
+
+  if (profiler->active_query == 0)
+    last_query_id = N_QUERIES - 1;
+  else
+    last_query_id = profiler->active_query - 1;
+
+  /* Advance iterator */
+  profiler->active_query += 1;
+  if (profiler->active_query == N_QUERIES)
+    profiler->active_query = 0;
+
+  /* If this is the first frame we already have a result */
+  if (profiler->first_frame)
+    {
+      profiler->first_frame = FALSE;
+      return 0;
+    }
+
+  glGetQueryObjectiv (profiler->gl_queries[last_query_id], GL_QUERY_RESULT_AVAILABLE, &res);
+  if (res == 1)
+    glGetQueryObjectui64v (profiler->gl_queries[last_query_id], GL_QUERY_RESULT, &elapsed);
+  else
+    elapsed = 0;
+
+  return elapsed;
+}
diff --git a/gsk/gskglprofilerprivate.h b/gsk/gskglprofilerprivate.h
new file mode 100644 (file)
index 0000000..51777c0
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __GSK_GL_PROFILER_PRIVATE_H__
+#define __GSK_GL_PROFILER_PRIVATE_H__
+
+#include <gsk/gsktypes.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_GL_PROFILER (gsk_gl_profiler_get_type ())
+G_DECLARE_FINAL_TYPE (GskGLProfiler, gsk_gl_profiler, GSK, GL_PROFILER, GObject)
+
+GskGLProfiler * gsk_gl_profiler_new                     (void);
+
+void            gsk_gl_profiler_begin_gpu_region        (GskGLProfiler *profiler);
+guint64         gsk_gl_profiler_end_gpu_region          (GskGLProfiler *profiler);
+
+G_END_DECLS
+
+#endif /* __GSK_GL_PROFILER_PRIVATE_H__ */
index 9e070e6e19a3c12205d7cc8c898937b9a2fd1d10..2ada40061b9b37229f242ea7aa25596b24c3b7f7 100644 (file)
@@ -5,6 +5,7 @@
 #include "gskdebugprivate.h"
 #include "gskenums.h"
 #include "gskgldriverprivate.h"
+#include "gskglprofilerprivate.h"
 #include "gskrendererprivate.h"
 #include "gskrendernodeprivate.h"
 #include "gskrendernodeiter.h"
@@ -89,7 +90,7 @@ struct _GskGLRenderer
   GQuark attributes[N_ATTRIBUTES];
 
   GskGLDriver *gl_driver;
-
+  GskGLProfiler *gl_profiler;
   GskShaderBuilder *shader_builder;
 
   int gl_min_filter;
@@ -421,6 +422,7 @@ gsk_gl_renderer_realize (GskRenderer *renderer)
 
   g_assert (self->gl_driver == NULL);
   self->gl_driver = gsk_gl_driver_new (self->context);
+  self->gl_profiler = gsk_gl_profiler_new ();
 
   GSK_NOTE (OPENGL, g_print ("Creating buffers and programs\n"));
   if (!gsk_gl_renderer_create_programs (self))
@@ -447,6 +449,7 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
   gsk_gl_renderer_destroy_buffers (self);
   gsk_gl_renderer_destroy_programs (self);
 
+  g_clear_object (&self->gl_profiler);
   g_clear_object (&self->gl_driver);
 
   if (self->context == gdk_gl_context_get_current ())
@@ -815,6 +818,7 @@ gsk_gl_renderer_render (GskRenderer *renderer,
   gboolean use_alpha;
   int status;
   guint i;
+  guint64 gpu_time;
 
   if (self->context == NULL)
     return;
@@ -837,6 +841,8 @@ gsk_gl_renderer_render (GskRenderer *renderer,
   if (!gsk_gl_renderer_validate_tree (self, root))
     goto out;
 
+  gsk_gl_profiler_begin_gpu_region (self->gl_profiler);
+
   gsk_gl_driver_begin_frame (self->gl_driver);
 
   /* Ensure that the viewport is up to date */
@@ -887,6 +893,10 @@ gsk_gl_renderer_render (GskRenderer *renderer,
   GSK_NOTE (OPENGL, g_print ("Drawing GL content on Cairo surface using a %s\n",
                              self->texture_id != 0 ? "texture" : "renderbuffer"));
 
+  gsk_gl_driver_end_frame (self->gl_driver);
+  gpu_time = gsk_gl_profiler_end_gpu_region (self->gl_profiler);
+  GSK_NOTE (OPENGL, g_print ("GPU time: %" G_GUINT64_FORMAT " nsec\n", gpu_time));
+
 out:
   use_alpha = gsk_renderer_get_use_alpha (renderer);
 
@@ -898,7 +908,6 @@ out:
                           0, 0, viewport.size.width, viewport.size.height);
 
   gdk_gl_context_make_current (self->context);
-  gsk_gl_driver_end_frame (self->gl_driver);
   gsk_gl_renderer_clear_tree (self);
 }